#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import hint
from .. import pilelib as stack
from .. import quickplayscore as quickplay
from .. import stackfill as fill
from ..forms import canfield as CanfieldLayout, klondike as KlondikeLayout
from ..gamelib import info as gi, state, game
from ..packlib import info  as cs, place
from ..piles import freecell as FreeCellStack, bristol as BristolStack
from ..utillib.misc import kwdefault

Wrap = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class Dover(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.CanfieldLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - reserve [3 vert S talon, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.DealReserveTalon
        Deal 1 card per reserve on click. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.bristol.Dover_RowStack (max_move=1)
        Init 3 open card per. Build down in rank alone. Play top. Base only
        from reserve.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.OpenStack
        Init 1 card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.DefaultHint
    """
    __info__ = dict(id=0x1398a, name='Dover', game_type=GT.FAN_TYPE, decks=2,
                    skill_level=SL.BALANCED, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = CanfieldLayout.CanfieldLayout
    _talon_ = stack.DealReserveTalon
    _foundation_ = Wrap(stack.SS_Foundation, max_move=0)
    _row_ = Wrap(BristolStack.Dover_RowStack, max_move=1)
    _reserve_ = stack.OpenStack

    def _create_game(self, **kw):
        """
        _create_game([rows=8, playcards=9, reserves=3, texts=False, align=LEFT])
          -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.canfield` for keyword definitions.
        """
        kwdefault(kw, rows=8, playcards=9, reserves=3, texts=False,
                  align=layout.LEFT)
        return super(Dover, self)._create_game(**kw)

    def _start(self, rows=3, flip=True, **kw):
        """
        _start([rows=3, flip=True]) -> None

        Deal 3 open card on row. Deal 1 card per reserve.
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class Spike(Dover):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.CanfieldLayout
        - row [7 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - reserve [3 vert S talon, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.DealReserveTalon
        Deal 1 card per reserve on click. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.AC_RowStack (base_card=KING)
        Init 1..7 card. Build down in rank by alt color. Play seq. Base king.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.OpenStack
        Init 1 card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.DefaultHint
    """
    __info__ = dict(id=0x13a54, name='Spike', game_type=GT.KLONDIKE, decks=1,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(stack.AC_RowStack, base_card=cs.KING)

    def _create_game(self, **kw):
        """
        _create_game([rows=7]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.canfield` for keyword definitions.
        """
        kw.setdefault('rows', 7)
        return super(Spike, self)._create_game(**kw)

    def _start(self, flip=False, **kw):
        """
        _start([rows=1, flip=True]) -> None

        Deal [1, 2, 3, 4, 5, 6, 7] card on row. Deal 1 card per reserve.
        """
        for i in range(1, len(self.s['rows'])):
            self.s['talon'].dealRow(rows=self.s['rows'][i:], flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class NewYork(Dover):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.CanfieldLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - reserve [3 vert S talon, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.OpenAutoTalon
        Deal 1 card per reserve on click. Build none. Play top. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Init base per 1. Build up in rank by same suit wrap. Play none. Base
        1st card.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.bristol.NewYork_RowStack (mod=13, max_move=1)
        Init 4 open card per. Build down in rank by alt color wrap. Play top.
        Base only from reserve or talon.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.freecell.FromTalonReserve (max_cards=MAX)
        Init 1 card per. Build any card from talon. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.SortHeldCautiousHint
    """
    __info__ = dict(id=0x13a29, name='New York', game_type=GT.FAN_TYPE,
                    decks=2, skill_level=SL.MOSTLY_SKILL, version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.OpenAutoTalon
    _foundation_ = Wrap(stack.Vary_SS_Foundation, max_move=0)
    _row_ = Wrap(BristolStack.NewYork_RowStack, mod=13, max_move=1)
    _reserve_ = Wrap(FreeCellStack.FromTalonReserve, max_cards=cs.MAX)
    _hint_ = hint.SortHeldCautiousHint

    def _create_game(self, **kw):
        """
        _create_game([texts=True]) -> Layout

        Add a canvas info label for base_rank next to last foundation. Add
        talon to dropstacks group.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.canfield` for keyword definitions.
        """
        kw.setdefault('texts', True)
        l, s = super(NewYork, self)._create_game(**kw), self.s
        self.texts['info'] = l.create_info_text(s['foundations'][-1], anchor='ne')
        self.sg['dropstacks'].append(s['talon'])
        return l

    def _start(self, rows=1, **kw):
        """
        _start([rows=1]) -> None

        Deal base on 1 foundation. Deal 1 open card per row. Deal 1 card per
        reserve. Update base rank for foundation. Flip top card on talon.
        """
        base_rank = self.s['talon'].TOPCARD['rank']
        self['talon'].dealBaseCards(ncards=1, saveinfo=False)
        self._save_stack_caps(stack, base_rank=base_rank)
        self._update_text()
        super(NewYork, self)._start(rows=rows, **kw)
        self.s['talon'].fill_stack()

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update canvas info label with base_rank of foundation.
        """
        if not self.texts['info']:
            return
        base_rank = self.s['foundations'][0].cap['base_rank']
        self.texts['info'].config(text='' if base_rank == cs.ANY
                                          else self.RANKS[base_rank])


class Gotham(NewYork):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.CanfieldLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - reserve [3 vert S talon, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.OpenAutoTalon
        Deal 1 card per reserve on click. Build none. Play top. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Init base per 1. Build up in rank by same suit wrap. Play none. Base
        1st card.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.bristol.Gotham_RowStack (mod=13)
        Init 3 open card per. Build down in rank alone wrap. Play sequence.
        Base only from reserve or talon.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.freecell.FromTalonReserve (max_cards=MAX)
        Init 1 card per. Build any card from talon. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.SortHeldCautiousHint
    """
    __info__ = dict(id=0x13a87, name='Gotham', game_type=GT.FAN_TYPE, decks=2,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(BristolStack.Gotham_RowStack, mod=13)

    def _start(self, **kw):
        """
        _start([rows=1]) -> None

        Deal base on 1 foundation. Deal 3 open card per row. Deal 1 card per
        reserve. Update base rank for foundation. Flip top card on talon.
        """
        kw.setdefault('rows', 3)
        super(Gotham, self)._start(**kw)


class Bristol(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [8 over 2 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - reserve [9 horz S, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.DealReserveTalon
        Deal 1 card per reserve on click. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack, base_rank=cs.GAP, max_move=1)
        Init 3 open card per with base king. Build down in rank alone. Play
        top. Base none.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.OpenStack
        Init 1 card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.bristol.Bristol_Hint
    """
    __info__ = dict(id=0x138aa, name='Bristol', game_type=(GT.FAN_TYPE |
                    GT.GNOME), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __credits__ = ['Albert Morehead', 'Geoffrey Mott-Smith']
    __version__ = 7.70

    _layout_ = KlondikeLayout.CrescentLayout
    _talon_ = stack.DealReserveTalon
    _foundation_ = Wrap(stack.SS_Foundation, max_move=0)
    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.GAP, max_move=1)
    _reserve_ = stack.OpenStack
    _hint_ = BristolStack.Bristol_Hint

    def _create_game(self, **kw):
        """
        _create_game([rows=8, ncols=4, playcards=9, reserves=9, texts=False])
          -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.crescent` for keyword definitions.
        """
        kwdefault(kw, rows=8, ncols=4, playcards=9, reserves=9, texts=False)
        return super(Bristol, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return KING for base _row_ deal.
        """
        return place.placeBaseCard(cards, len(self.s['rows']))

    def _start(self, rows=3, flip=True, **kw):
        """
        _start([rows=3, flip=True]) -> None

        Deal 3 open card on row. Deal 1 card per reserve.
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class Belvedere(Bristol):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [8 over 2 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - reserve [9 horz S, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.DealReserveTalon
        Deal 1 card per reserve on click. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Init ace per. Build up in rank by same suit. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack, base_rank=cs.GAP, max_move=1)
        Init 3 open card per with base king. Build down in rank alone. Play
        top. Base none.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.OpenStack
        Init 1 card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.bristol.Bristol_Hint
    """
    __info__ = dict(id=0x13956, name='Belvedere', game_type=GT.FAN_TYPE,
                    decks=1, skill_level=SL.MOSTLY_SKILL, version='4.60')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return single ACE for _foundation_ deal and KING for base
        _row_.
        """
        for c in cards[:]:
            if c['rank'] == cs['ACE']:
                cards.remove(c)
                break
        cards = super(Belvedere, self)._pack_place(cards)
        return cards[:-24] + [c] + cards[-24:]

    def _start(self, rows=3, flip=True, **kw):
        """
        _start([rows=3, flip=True]) -> None

        Deal 3 open card on row. Deal ace per 1 foundaiton. Deal 1 card per
        reserve.
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealBaseCards(ncards=1, saveinfo=False)
        self.s['talon'].deal_cards()


class Interment(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.InternmentLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - x-wastes [3 S talon, no offset]
        - reserve [6 E, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.OpenAutoTalon
        Build none. Play top. No redeal.

      _xwastes_ : pysollib.stack.Stack
        .. pysollib.stacks.freecell.FromTalonReserve (max_cards=MAX)
        Build any. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation (max_move=0)
        Init base per 1. Build up in rank by same suit. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SS_RowStack (max_move=1)
        Init 1 card per. Build down in rank by same suit. Play top.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.freecell.NoFlipReserve, pysollib.stack.OpenStack
        Init 13 card per 1st NE, 1 card per remain. Build none. Play top. Gap
       E deal 1 card from SW 1st NE reserve.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.bristol.Interment_Hint
    """
    __info__ = dict(id=0x13adc, name='Interment', game_type=GT.FAN_TYPE,
                    decks=2, skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = CanfieldLayout.InternmentLayout
    _talon_ = stack.OpenAutoTalon
    _xwaste_ = Wrap(FreeCellStack.FromTalonReserve, max_cards=cs.MAX)
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(stack.SS_RowStack, max_move=1)
    _reserves_ = (FreeCellStack.NoFlipReserve, stack.OpenStack,)
    _hint_ = BristolStack.Interment_Hint

    def _create_game(self, **kw):
        """
        _create_game([rows=8, ncols=8, xwastes=3, xwastestext=True, reserves=6,
          reserve_texts=False]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.crescent` for keyword definitions.
        """
        l, s = self._layout_(self), self.s
        kwdefault(kw, rows=8, ncols=8, xwastes=3, xwastestext=True,
                  reserves=6, reserve_texts=False)
        l.create(**kw)
        s['talon'] = self._talon_(l.s['talon'].x, l.s['talon'].y, game=self)
        for r in l.s['foundations']:
            s['foundations'].append(self._foundation_(r.x, r.y, game=self,
                                                   suit=r.suit))
        for r in l.s['xwastes']:
            s['xwastes'].append(self._xwaste_(r.x, r.y, game=self))
        for r in l.s['rows']:
            s['rows'].append(self._row_(r.x, r.y, game=self))
        r = l.s['reserve'][0]
        s['reserves'].append(self._reserves_[0](r.x, r.y, game=self))
        l.set_ncards_text(s['reserves'][0], 'nw')
        for r in l.s['reserves'][1:]:
            s['reserves'].append(self._reserves_[1](r.x, r.y, game=self))
        l.defaultAll()
        self.sg['dropstacks'] += s['xwastes']
        self.sg['openstacks'] += s['xwastes']
        self.sg['dropstacks'].append(s['talon'])
        return l

    def _start(self, reserve_talon=13, **kw):
        """
        _start([rows=3, flip=True]) -> None

        Deal 13 close card on 1st NE, 1 card on remain reserve. Deal 1 per
        row. Flip talon top.
        """
        s = self.s
        for i in range(reserve_talon):
            s['talon'].dealRow(rows=[s['reserves'][0]], flip=False, frames=0)
        s['talon'].dealRow(rows=s['reserves'][1:], frames=0)
        self._play_sound('deal')
        s['talon'].dealRow()
        s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        While 1st NE reserve has cards, fill remain empty reserve with 1 of
        its card. Fill empty row from talon.

        *Requires*:
          `pysollib.stackfill.fromTalon` to fill empty row.
        """
        if not stack.cards:
            fill.fromTalon(self, stack, self.s['rows'])
            if stack in self.s['reserves'][1:]:
                from_stack = self.s['reserves'][0]
                if from_stack.cards:
                    self.fill(from_stack, stack)

    _quickplay_ = quickplay.XWasteStack


PROVIDES = [Bristol, Belvedere, Dover, NewYork, Spike, Gotham, Interment]